#include <cstdint>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

#include <nop/serializer.h>
#include <nop/structure.h>
#include <nop/utility/stream_reader.h>
#include <nop/utility/stream_writer.h>

namespace example {

struct Person {
  std::string name;
  std::uint32_t age_years;
  std::uint8_t height_inches;
  std::uint16_t weight_pounds;
  std::vector<int> number;

  std::string getName() { return name; }
  void setName(std::string &n) { name = n; }

  std::uint32_t getAge() { return age_years; }
  void setAge(std::uint32_t &a) { age_years = a; }

  std::uint8_t getHeightInches() { return height_inches; }
  void setHeightInches(std::uint8_t &hi) { height_inches = hi; }

  std::uint16_t getWeightPounds() { return weight_pounds; }
  void setWeightPounds(std::uint16_t &wp) { weight_pounds = wp; }

  std::vector<int> getNumber() { return number; }
  void setNumber(std::vector<int> &n) { number = n; }

  NOP_STRUCTURE(Person, name, age_years, height_inches, weight_pounds, number);
};

} // namespace example

int main(int argc, char **argv) {
  using Writer = nop::StreamWriter<std::stringstream>;
  nop::Serializer<Writer> serializer;

  example::Person john{"John Doe", 37, 72, 180, {1, 2, 3, 4}};
  serializer.Write(john);
  const std::string data = serializer.writer().stream().str();
  std::cout << "Wrote " << data << " "<<data.size() << " bytes." << std::endl;

  // Create a deserializer around a std::stringstream with the serialized data.
  using Reader = nop::StreamReader<std::stringstream>;
  nop::Deserializer<Reader> deserializer{serializer.writer().take()};

  example::Person person;
  deserializer.Read(&person);
  std::cout << "person:  " << person.name << " " << person.age_years << " "
            << person.height_inches << " " << person.weight_pounds << " "
            << person.number[1] << std::endl;

  std::cout << "person:  " << person.getName() << " " << person.getAge() << " "
            << person.getHeightInches() << " " << person.getWeightPounds()
            << " " << person.getNumber()[1] << std::endl;
  return 0;
}
